This page last changed on Aug 23, 2008 by rosspatterson.

The CCNet configuration preprocessor acts on the ccnet.config file. Preprocessor directives are specified in the XML namespace "urn:ccnet.config.builder" to distinguish them from ordinary configuration markup. Any markup not in the preprocessor's namespace are passed through unchanged.

Preparing to Use the Preprocessor

The configuration preprocessor is invoked by declaring an XML Namespace of "urn:ccnet.config.builder" on the root configuration element ("<cruisecontrol>"). The namespace abbreviation you choose must be specified on any preprocessor directives you use. For the rest of this explanation, we will specify it as "cb", a mnemonic for "configuration builder".

<cruisecontrol xmlns:cb="urn:ccnet.config.builder">

Preprocessor Elements

The configuration preprocessor has several elements that control its processing of your configuration.

  • The <define> element is used to define constants to be expanded later.
  • The <include> element is used to include the contents of another file.
  • The <scope> element is used to encapsulate sections that change the value of an existing constant.

Defining Preprocessor Constants

The <define> element is used to define a preprocessor constant. It can be used in several ways:

Text constants (values are strings):

Define a constant named "foo" with a value of "bar":

<cb:define foo="bar"/>

You can define more than one constant in the same define element:

<cb:define a="1" b="2" c="3"/>

Nodeset constants (values are XML fragments):

Define a constant named "baz" with a value of an xml fragment:

<cb:define name="baz"> 
  <some_element>
    <some_inner_element/>
  </some_element>
</cb:define>

Any valid XML inside the define element is considered to be the constant's value. This includes elements, attributes, text nodes, and comments.

Expanding Preprocessor Constant Values

Once defined, preprocessor constants can be expanded in two ways: as text references or as XML references.

Text References

References of the form "$(const_name)" which are found in attribute values or text nodes will be expanded such that the text value replaces the reference. If no constant exists with the given name and there exists a windows environment variable with the same name, that environment variable's value will be used.

Examples

Use in an attribute:

<cb:define foo="bar"/>
<somexml attr1="$(foo)"/>

expands to:

<somexml attr1="bar"/>

Use as text of an element:

<cb:define foo="bar"/>
<somexml>$(foo)</somexml>

expands to:

<somexml>bar</somexml>

Use of Windows environment variables:

<env dir="$(PATH)"/>

expands to:

<env dir="... your PATH environment value ..."/>

XML References

When the preprocessor encounters an element in the preprocessor namespace, and the element name is not one of the predefined keywords (define,scope,config-template, etc), the element will be replaced by the constant value associated with the element name.

Examples

Use as text of an element:

<cb:define foo="bar"/>
<sample>
  <cb:foo/>
</sample>

expands to:

<sample>
  bar
</sample>

Uses as sub-element:

<cb:define name="baz"> 
  <some_element>
    <some_inner_element/>
  </some_element>
</cb:define>
<sample>
  <cb:baz/>
</sample>

expands to:

<sample>
  <some_element>
    <some_inner_element/>
  </some_element>
</sample>

If a constant reference refers to a constant which has not been defined and which does not exist as an OS environment variable, an error will occur.

Nested Expansions and Parameters

Constant references can be nested, i.e., the value of constant "zed" can contain a reference to constant "alpha".

<cb:define alpha="alphaval"/>
<cb:define zed="zedval/$(alpha)"/>

<z>$(zed)</z>

expands to:

<z>zedval/alphaval</z>

In addition, using the cb:varname call syntax outlined above, constant values can be passed as part of the call element. Consider the following definition, in which neither "gamma" nor "delta" have yet been defined:

<cb:define name="beta">
  <hello>
    <cb:gamma/>  
    <hi attr1="$(delta)"/>
  </hello>
</cb:define>

Since gamma and delta have not been defined, they must be passed in with the reference to beta. This is done as follows:

<cb:beta delta="deltaval">
  <cb:define name="gamma">
    <gamma_element>hi</gamma_element>
  </cb:define>
</cb:beta>

This will expand to:

<hello>
  <gamma_element>hi</gamma_element>
  <hi attr1="deltaval" />
</hello>

Scopes

The <scope> element can be used to control the scope of a preprocessor definition. You may not define the same constant twice within the same scope, however you may introduce a nested scope which redefines a particular value in an outer scope. A scope is semantically equivalent to a stack frame in traditional programming terms.

<cb:scope a="a_val" b="b_val">
  <test attr="$(a)" attr2="$(b)"/>
  <cb:scope a="a_val_redefined">
    <test attr="$(a)" attr2="$(b)"/>
  </cb:scope>
</cb:scope>

expands to:

<test attr="a_val" att2="b_val"/>
<test attr="a_val_redefined" att2="b_val"/>

Comments

XML Comments whose first character is '#' will not be copied to the output. All other comments will.

<!-- This comment will appear in the output file-->
<!--# This comment will not -->

Including files

The <include> element is used to include the contents of another file. The element is replaced with the contents of that file, which must be a valid XML document. The file is specified as a URL, relative to the file that contains the <include> element.

Assuming that the CCNet configuration file "ccnet.config" includes the file "projects\project.config":

... lines before ...
<cb:include href="projects/project.config"/>
... lines after ...

and that the "projects\project.config" file includes a file called "EmailConfig.xml" from the same directory as "ccnet.config":

<project>
  ... project definition ...
  <publishers>
    <cb:include href="../EMailConfig.xml"/>
    ... more publishers ...
  </publishers>
</project>

and that the "EMailConfig.xml" file contains an Email Publisher definition:

<email>
  ... email configuration info ...
</email>

the results would be:

... lines before ...
<project>
  ... project definition ...
  <publishers>
    <email>
      ... email configuration info ...
    </email>
    ... more publishers ...
  </publishers>
</project>
... lines after ...

Common Problems

XmlException: "There are multiple root elements" when including another file

The <include> directive requires the included file to be a valid XML document. All XML documents must have exactly one "root element" - i.e., there must be an outermost pair of start/end XML tags. The XML file being included in this case has more than one of these root elements (e.g., <project name="A">...</project> <project name="B">...</project>). The file must either be split apart so that each root element is in a separate file, or the group of root elements must be enclosed in a single outer element.

Preprocessor directives don't work in an included file

XML requires that all "namespace" definitions (i.e., xmlns:xxx attributes) must be declared in every file where they're used. Every included file must have an xmlns:cb="urn:ccnet.config.builder" attribute on its root element, even if the namespace was defined in the file that included it.

Document generated by Confluence on Mar 14, 2009 02:55